import * as WebSocket from 'ws';
import { WebSocketAdapter, INestApplicationContext } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable, fromEvent, EMPTY } from 'rxjs';
import { mergeMap, filter } from 'rxjs/operators';

export class WsAdapter implements WebSocketAdapter {

    /**
     * 构造函数，用于初始化类实例
     * 
     * @param app INestApplicationContext类型的参数，代表NestJS应用上下文
     */
    constructor(private app: INestApplicationContext) { }

    /**
     * 创建一个WebSocket服务器
     * 
     * @param port - 服务器监听的端口号这个参数是必须的
     * @param options - 可选的配置对象可以在这里传递WebSocket.Server支持的任何选项
     * @returns 返回一个WebSocket.Server实例方便进一步的配置和使用
     * 
     * 注意: 此函数允许外部创建并配置WebSocket服务器通过指定端口和其他可选参数
     */
    create(port: number, options: any = {}): any {
        console.log(`WebSocket服务创建成功！监听端口为${port}，请访问ws://localhost:${port}`);
        return new WebSocket.Server({ port, ...options });
    }

    /**
     * 绑定客户端连接事件
     * 
     * 该函数用于在WebSocket服务器上绑定一个连接事件监听器当有客户端连接到服务器时，会调用传入的回调函数
     * 主要用于处理新建立的客户端连接，使服务器能够响应客户端的连接事件
     * 
     * @param server WebSocket服务器实例，用于监听客户端的连接
     * @param callback 当有客户端连接时，将调用此回调函数允许执行自定义逻辑来处理新连接
     */
    bindClientConnect(server, callback: Function) {
        // 输出服务器实例信息，便于调试和日志记录
        // console.log('ws bindClientConnect, server:\n', server);
        // 绑定连接事件监听器，当有客户端连接时，调用传入的回调函数
        server.on('connection', callback);
    }

    /**
     * 绑定消息处理程序
     * 
     * 此方法用于处理WebSocket客户端的新连接，并根据提供的消息映射属性和处理程序来处理消息
     * 它监听客户端的消息事件，并通过指定的处理程序来处理这些消息
     * 
     * @param client WebSocket客户端，代表新的连接
     * @param handlers 消息映射属性数组，用于确定如何处理不同类型的消息
     * @param process 消息处理函数，将接收到的消息转换为Observable，以便进一步处理
     */
    bindMessageHandlers(
        client: WebSocket,
        handlers: MessageMappingProperties[],
        process: (data: any) => Observable<any>,
    ) {
        // 日志输出，表示有新的连接
        console.log('[waAdapter]有新的连接进来')

        // 使用fromEvent操作符监听WebSocket客户端的消息事件
        fromEvent(client, 'message')
            .pipe(
                // 使用mergeMap操作符对每个消息事件的数据进行处理
                mergeMap(data => this.bindMessageHandler(client, data, handlers, process)),
                // 使用filter操作符过滤掉未通过处理程序处理的数据
                filter(result => result),
            )
            // 订阅处理后的响应，并发送回客户端
            .subscribe(response => client.send(JSON.stringify(response)));
    }

    /**
     * 绑定消息处理程序
     * 
     * 该函数接收WebSocket客户端、消息缓冲区、消息处理程序映射数组和处理函数作为参数
     * 它负责解析缓冲区中的消息，查找对应的消息处理程序，并用处理函数处理消息
     * 如果解析失败或找不到对应的消息处理程序，函数将返回一个空的Observable
     * 
     * @param client WebSocket客户端，用于发送和接收消息
     * @param buffer 消息缓冲区，包含待处理的消息数据
     * @param handlers 消息处理程序数组，每个处理程序包含消息类型和回调函数
     * @param process 处理函数，用于处理消息并返回Observable
     * @returns 返回处理结果的Observable，如果解析失败或找不到处理程序则返回空的Observable
     */
    bindMessageHandler(
        client: WebSocket,buffer,
        handlers: MessageMappingProperties[],
        process: (data: any) => Observable<any>,
    ): Observable<any> {
        let message = null;
        try {
            // 尝试解析缓冲区中的数据为JSON格式
            message = JSON.parse(buffer.data);
        } catch (error) {
            // 如果解析出错，打印错误信息并返回空的Observable
            console.log(`ws解析json出错,原始数据：${buffer.data}`);
            return EMPTY;
        }

        // 查找与解析后的消息事件匹配的消息处理程序
        const messageHandler = handlers.find(
            handler => handler.message === message.event,
        );
        if (!messageHandler) {
            // 如果没有找到匹配的消息处理程序，返回空的Observable
            return EMPTY;
        }
        // 使用处理函数处理消息处理程序的回调，并返回结果的Observable
        return process(messageHandler.callback(message.data));
    }

    /**
     * 关闭WebSocket服务器
     * 
     * 此方法用于关闭指定的WebSocket服务器它打印一条消息到控制台，
     * 然后调用服务器的close方法来关闭服务器
     * 
     * @param server WebSocket服务器实例，需要被关闭
     */
    close(server) {
        console.log('WebSocket 服务关闭'); // 打印关闭消息
        server.close(); // 关闭服务器
    }
}
